package com.qsxc.system.aspect;

import cn.hutool.core.util.StrUtil;
import com.qsxc.system.annotation.RedisLock;
import com.qsxc.utils.BeanUtil;
import com.qsxc.utils.StringUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * @author chenhongwei
 */
@Aspect
@Component
@Order
public class RedisLockAspect {

    private RedissonClient redissonClient;

    private static final String REDISSON_LOCK_PREFIX = "redisson_lock:";

    @Autowired
    public RedisLockAspect(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @Around("@annotation(redisLock)")
    public Object around(ProceedingJoinPoint joinPoint, RedisLock redisLock) throws Throwable {
        String lockName = redisLock.lockGroupName();
        RLock rLock = redissonClient.getLock(getRedisKey(redisLock, joinPoint, lockName));
        rLock.lock(redisLock.expire(), redisLock.timeUnit());
        Object result = null;
        try {
            //执行方法
            result = joinPoint.proceed();
        } finally {
            if (rLock.isHeldByCurrentThread()) {
                rLock.unlock();
            }
            // 延迟500毫秒结束，由于DRDS数据库保存有一定的延迟
            rLock.lock(200, TimeUnit.MILLISECONDS);
        }
        return result;
    }

    /**
     * 将spel表达式转换为字符串
     *
     * @param redisLock
     * @param joinPoint 切点
     * @return redisKey
     */
    private String getRedisKey(RedisLock redisLock, ProceedingJoinPoint joinPoint, String lockName) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method targetMethod = methodSignature.getMethod();
        StringBuilder sb = new StringBuilder();
        sb.append(REDISSON_LOCK_PREFIX + lockName);

        if (!StringUtil.isEmpty(lockName)) {
            sb.append(StrUtil.COLON).append(lockName);
        } else {
            sb.append(StrUtil.COLON).append(targetMethod.getName());
        }

        Object[] arguments1 = joinPoint.getArgs();
        if (!StringUtil.isEmpty(redisLock.field()) && arguments1.length > 0) {
            Object value = BeanUtil.getProperty(arguments1[0], redisLock.field());
            if (value != null) {
                sb.append(StrUtil.UNDERLINE).append(redisLock.field()).append(StrUtil.UNDERLINE).append(value);
            }
        }
        return sb.toString();
    }
}
